﻿using System;

namespace Levfier.Proccessors
{
    //共通の計算クラス
    public class CommonMath
    {

        /// <summary>
        /// sinc関数
        /// </summary>
        /// <param name="value">入力値</param>
        /// <returns>sinc</returns>
        static public double Sinc(double value)
        {
            if (0 == value)
            {
                return 1.0d;
            }
            return Math.Sin(value) / value;
        }

        /// <summary>
        /// ハミング窓の値を取得する
        /// </summary>
        /// <param name="index">求める値のインデックス</param>
        /// <param name="size">窓のサイズ</param>
        /// <returns>ハミング窓の値</returns>
        static public double GetHammingWindowValue(int index, int size)
        {
            return 0.54 - 0.46 * Math.Cos(2 * Math.PI * (double)index / (double)size);
        }

        /// <summary>
        /// ハミング窓の値を取得する
        /// </summary>
        /// <param name="index">求める値のインデックス</param>
        /// <param name="size">窓のサイズ</param>
        /// <returns>ハミング窓の値</returns>
        static public double GetHanningWindowValue(int index, int size)
        {
            return 0.5 - 0.5 * Math.Cos(2 * Math.PI * (double)index / (double)size);
        }

        /// <summary>
        /// 高速フーリエ変換
        /// </summary>
        /// <param name="inputRe"></param>
        /// <param name="inputIm"></param>
        /// <param name="outputRe"></param>
        /// <param name="outputIm"></param>
        /// <param name="bitSize"></param>
        public static void FFT(double[] inputRe, double[] inputIm, out double[] outputRe, out double[] outputIm, int bitSize)
        {
            int dataSize = 1 << bitSize;
            int[] reverseBitArray = BitScrollArray(dataSize);

            outputRe = new double[dataSize];
            outputIm = new double[dataSize];

            // バタフライ演算のための置き換え
            for (int i = 0; i < dataSize; i++)
            {
                outputRe[i] = inputRe[reverseBitArray[i]];
                outputIm[i] = inputIm[reverseBitArray[i]];
            }

            // バタフライ演算
            for (int stage = 1; stage <= bitSize; stage++)
            {
                int butterflyDistance = 1 << stage;
                int numType = butterflyDistance >> 1;
                int butterflySize = butterflyDistance >> 1;

                double wRe = 1.0;
                double wIm = 0.0;
                double uRe = System.Math.Cos(System.Math.PI / butterflySize);
                double uIm = -System.Math.Sin(System.Math.PI / butterflySize);

                for (int type = 0; type < numType; type++)
                {
                    for (int j = type; j < dataSize; j += butterflyDistance)
                    {
                        int jp = j + butterflySize;
                        double tempRe = outputRe[jp] * wRe - outputIm[jp] * wIm;
                        double tempIm = outputRe[jp] * wIm + outputIm[jp] * wRe;
                        outputRe[jp] = outputRe[j] - tempRe;
                        outputIm[jp] = outputIm[j] - tempIm;
                        outputRe[j] += tempRe;
                        outputIm[j] += tempIm;
                    }
                    double tempWRe = wRe * uRe - wIm * uIm;
                    double tempWIm = wRe * uIm + wIm * uRe;
                    wRe = tempWRe;
                    wIm = tempWIm;
                }
            }
        }

        /// <summary>
        /// 高速フーリエ変換
        /// </summary>
        /// <param name="inputRe"></param>
        /// <param name="inputIm"></param>
        /// <param name="outputRe"></param>
        /// <param name="outputIm"></param>
        /// <param name="bitSize"></param>
        public static void IFFT(double[] inputRe, double[] inputIm, out double[] outputRe, out double[] outputIm, int bitSize)
        {
            double[] conjugationIm = new double[inputIm.Length];

            double frameSize = conjugationIm.Length;

            //(1)複素共役を取り、 \overline{F(t)} を求める、
            for (int i = 0; i < frameSize; i++)
            {
                conjugationIm[i] = -inputIm[i];
            }

            //(2)高速フーリエ変換
            FFT(inputRe, conjugationIm, out outputRe, out outputIm, bitSize);

            //(3)その結果の複素共役を取り、Nで割る
            for (int i = 0; i < frameSize; i++)
            {
                outputRe[i] = outputRe[i] / frameSize;
                outputIm[i] = -outputIm[i] / frameSize;
            }
        }

        /// <summary>
        /// ビットを左右反転した配列を返す
        /// </summary>
        /// <param name="arraySize"></param>
        /// <returns></returns>
        private static int[] BitScrollArray(int arraySize)
        {
            int[] reBitArray = new int[arraySize];
            int arraySizeHarf = arraySize >> 1;

            reBitArray[0] = 0;
            for (int i = 1; i < arraySize; i <<= 1)
            {
                for (int j = 0; j < i; j++)
                    reBitArray[j + i] = reBitArray[j] + arraySizeHarf;
                arraySizeHarf >>= 1;
            }
            return reBitArray;
        }
    }
}
